package com.osdp.gateway.service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.osdp.gateway.config.OsdpGatewayProperties;
import com.osdp.gateway.exception.RouteException;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;

@Slf4j
@Service
public class GatewayRouteService {
    @Autowired
    private OsdpGatewayProperties gatewayProperties;

    @Autowired
    private GatewayDiscoveryRouteService gatewayDiscoveryRouteService;

    @Autowired
    private IDynamicRouteService dynamicRouteService;

    @Autowired
    private INacosService nacosService;

    @Autowired
    private DiscoveryClient discoveryClient;

    private final static List<String> ROMOVED_DISCOVERY_ROUTES = new CopyOnWriteArrayList<String>();

    private final static String DISCOVERY_ROUTE_PRIFIX = "system";

    @Value("${osdp.gateway.discovery.enabled}")
    private boolean discoveryEnabled;

    /**
     * 获取配置
     * @return
     */
    private String getConfigService(){
        return nacosService.getConfig(gatewayProperties.getServerAddr(),gatewayProperties.getNamespace(),
                gatewayProperties.getGroup(),gatewayProperties.getDataId());
    }

    /**
     *  读取Nacos路由的配置信息
     * @return List<RouteDefinition>
     */
    private List<RouteDefinition> getNacosRouteDefinitions(){
        String config = getConfigService();
        if(StringUtils.isEmpty(config)) return Lists.newArrayList();
        List<RouteDefinition> routes = JSONArray.parseArray(config,RouteDefinition.class);
        return routes;
    }

    /**
     * 得到所有路由配置
     * @return
     */
    public List<RouteDefinition> getRouteDefinitions(){
        List<RouteDefinition> list = getNacosRouteDefinitions();
        for(RouteDefinition route : list){
            String id = StringUtils.substringAfter(route.getId(),":");
            route.setId(id);
        }
        return list;
    }

    /**
     * 根据租户id得到路由配置
     * @param namespace   tenant租户ID
     * @return List<RouteDefinition>
     */
    public List<RouteDefinition> getRouteDefinitions(String namespace){
        List<RouteDefinition> routeDefinitions = getNacosRouteDefinitions();
        List<RouteDefinition> routes = Lists.newArrayList();
        for(RouteDefinition route : routeDefinitions){
            String tenantId = StringUtils.substringBefore(route.getId(),":");//租户ID
            if(StringUtils.equals(namespace,tenantId)){
                String id = StringUtils.substringAfter(route.getId(),":");
                route.setId(id);
                routes.add(route);
            }
        }
        return routes;
    }

    /**
     * 根据路由ID得到配置
     * @param routeId
     * @return
     */
    public RouteDefinition getRouteDefinition(String routeId){
        List<RouteDefinition> routeDefinitions = getNacosRouteDefinitions();
        for(RouteDefinition route : routeDefinitions){
            String id = StringUtils.substringAfter(route.getId(),":");  //route ID
            if(StringUtils.equals(routeId,id)){
                route.setId(id);
                return route;
            }
        }
        return null;
    }

    private Map<String,RouteDefinition> getNacosRouteDefinitionMap(){
        Map<String,RouteDefinition> routeMap = Maps.newHashMap();
        List<RouteDefinition> routes = getNacosRouteDefinitions();
        if(CollectionUtils.isEmpty(routes)) return routeMap;
        for(RouteDefinition route : routes){
            routeMap.put(route.getId(),route);
        }
        return routeMap;
    }
    /**
     * 发布配置
     * PrettyFormat: 结果是否格式化,默认为false
     * WriteMapNullValue: 是否输出值为null的字段,默认为false
     * WriteDateUseDateFormat: 全局修改日期格式,默认为false。JSON.DEFFAULT_DATE_FORMAT = “yyyy-MM-dd”;JSON.toJSONString(obj, SerializerFeature.WriteDateUseDateFormat);
     * @param routes
     * @return
     */
    private boolean saveRouteDefinitions(Collection<RouteDefinition> routes){
        String content = JSONArray.toJSONString(routes, SerializerFeature.PrettyFormat,
                SerializerFeature.WriteMapNullValue,SerializerFeature.WriteDateUseDateFormat);
        return nacosService.saveConfig(gatewayProperties.getServerAddr(),"",gatewayProperties.getGroup(),
                gatewayProperties.getDataId(),content);
    }

    /**
     * 新增路由定义
     * @param namespace
     * @param routeDefinition
     */
    public void addRouteDefinition(String namespace, RouteDefinition routeDefinition){
        if(routeDefinition == null) throw new RouteException("routeDefinition 不能为空");
        List<RouteDefinition>  routes = getNacosRouteDefinitions();
        for(RouteDefinition route : routes){
            String id = StringUtils.substringAfter(route.getId(),":");  //route ID
            if(StringUtils.equals(id,routeDefinition.getId())){
                throw new RouteException(String.format("% 路由已经定义。",id));
            }
        }
        String id = String.format("%:%",namespace,routeDefinition.getId());
        routeDefinition.setId(id);
        routes.add(routeDefinition);
        saveRouteDefinitions(routes);
    }

    /**
     * 增加配置信息
     * @param routeDefinition
     */
    public void addRouteDefinition(RouteDefinition routeDefinition){
        if(routeDefinition == null) throw new RouteException("routeDefinition 不能为空");
        List<RouteDefinition> routes = getNacosRouteDefinitions();
        routes.add(routeDefinition);
        saveRouteDefinitions(routes);
    }

    /**
     * 更新配置信息
     * @param routeDefinition
     */
    public void updateRouteDefinition(RouteDefinition routeDefinition){
        if(routeDefinition == null) throw new RouteException("routeDefinition 不能为空");
        Map<String,RouteDefinition> routeMap = getNacosRouteDefinitionMap();
        for(RouteDefinition route : routeMap.values()){
            String id = StringUtils.substringAfter(route.getId(),":");  //route ID
            if(StringUtils.equals(id, routeDefinition.getId())){
                routeDefinition.setId(id);
                routeMap.put(routeDefinition.getId(),routeDefinition);
            }
        }
        saveRouteDefinitions(routeMap.values());
    }

    /**
     * 删除路由配置
     * @param routeId
     */
    public void removeRouteDefinition(String routeId){
        if(StringUtils.isEmpty(routeId)) throw new RouteException("route Id 不能为空。");
        List<RouteDefinition> routes = getNacosRouteDefinitions();
        List<RouteDefinition> routeList = Lists.newArrayList();
        for(RouteDefinition route : routes){
            String id = StringUtils.substringAfter(route.getId(),":");  //route ID
            if(StringUtils.equals(id,routeId)){
                String namespace = StringUtils.substringBefore(route.getId(),":");
                if(StringUtils.equals(namespace,DISCOVERY_ROUTE_PRIFIX)){
                    ROMOVED_DISCOVERY_ROUTES.add(id);
                } else {
                    routeList.add(route);
                }
            }
        }
        saveRouteDefinitions(routeList);
    }

    /**
     * 得到删除后的服务ID
     * @return
     */
    private List<String> getService(){
        //All known service IDs
        List<String> service = discoveryClient.getServices();
        service.removeAll(ROMOVED_DISCOVERY_ROUTES);
        return service;
    }

    /**
     * 保存发现路由
     * @param gatewayList
     */
    public void saveDiscoveryRoutes(CopyOnWriteArrayList<RouteDefinition> gatewayList){
        if(!discoveryEnabled) return;
        List<String> serviceIds = getService();
        for(String serviceId : serviceIds){
            boolean existed = false;
            for(RouteDefinition route : gatewayList){
                if(StringUtils.equals(serviceId,route.getId())){
                    existed =true;
                    break;
                }
            }
            if(!existed){
                RouteDefinition routeDefinition = gatewayDiscoveryRouteService.initRouteDefinition(serviceId);
                gatewayList.add(routeDefinition);
                String id = String.format("%s:%s",DISCOVERY_ROUTE_PRIFIX,serviceId);
                RouteDefinition sysRouteDefinition = gatewayDiscoveryRouteService.initRouteDefinition(serviceId);
                sysRouteDefinition.setId(id);
                addRouteDefinition(sysRouteDefinition);//添加到Nacos路由配置中
            }
        }
    }

    /**
     * 根据服务
     * @param services
     * @return
     */
    public Map<String,String> getServicePath(List<String> services){
        if(CollectionUtils.isEmpty(services)) return Collections.emptyMap();
        Map<String,String> resultMap = Maps.newHashMap();
        List<RouteDefinition> routeDefinitions = getNacosRouteDefinitions();
        for(RouteDefinition routeDefinition: routeDefinitions){
            String id = StringUtils.substringBefore(routeDefinition.getId(),":");
            if(StringUtils.equalsIgnoreCase(id,"system")){
                continue;
            }
            String uri = routeDefinition.getUri().toString();
            String service = StringUtils.substringAfter(uri,"lb://");
            if(services.contains(service)){
                List<PredicateDefinition> predicates = routeDefinition.getPredicates();
                for(PredicateDefinition predicate : predicates){
                    String pattern = predicate.getArgs().get("Pattern");
                    if(pattern.isEmpty()){
                        resultMap.put(service,pattern);
                    }else{
                        resultMap.put(service, predicate.getArgs().get("_genkey_0"));
                    }
                }
            }
        }
        return resultMap;
    }
    //-----------------------------租户管理------------------------------//
    /**
     * 一个租户对应多个角色
     * 根据租户ID查找所有角色ID
     * @param tenantIds  租户ID
     * @return
     */
    public List<String> getRouteIds(List<String> tenantIds){
        List<String> resultList = Lists.newArrayList();
        Map<String,RouteDefinition> routeMap = this.getNacosRouteDefinitionMap();
        for(String key : routeMap.keySet()){
            String tenantId = StringUtils.substringBefore(key,":");
            if(tenantIds.contains(tenantId)){
                String routeId = StringUtils.substringAfter(key,":");
                resultList.add(routeId);
            }
        }
        if(resultList == null){
            return Collections.emptyList();
        }
        return resultList;
    }

    /**
     * 根据租户ID查收 对应的路由配置
     * @param tenantIds
     * @return
     */
    public List<RouteDefinition> getTenantRouteDefinitions(Collection<String> tenantIds){
        List<RouteDefinition> resultList = Lists.newArrayList();
        List<RouteDefinition> routeDefinitionList = getNacosRouteDefinitions();
        for(RouteDefinition route : routeDefinitionList){
            String tenantId = StringUtils.substringBefore(route.getId(),":");
            if(tenantIds.contains(tenantId)){
                String id = StringUtils.substringAfter(route.getId(),":");
                route.setId(id);
                resultList.add(route);
            }
        }
        return resultList;
    }

    /**
     * 
     * @param tenantId
     * @param id
     * @return
     */
    public RouteDefinition getTenantRouteDefinition(String tenantId,String id){
        String routeId = String.format("%:%",tenantId,id);
        Map<String,RouteDefinition> routeDefinitionMap = getNacosRouteDefinitionMap();
        RouteDefinition routeDefinition = routeDefinitionMap.get(routeId);
        if(routeDefinition == null) return null;
        routeDefinition.setId(id);
        return routeDefinition;
    }

    /**
     * 添加租户路由定义
     * @param tenantId
     * @param routeDefinition
     */
    public void addTenantRouteDefinition(String tenantId,RouteDefinition routeDefinition){
        if(routeDefinition == null) throw new RouteException("routeDefinition 不能为空");
        List<RouteDefinition> routeList = getNacosRouteDefinitions();
        for(RouteDefinition route : routeList){
            String id = StringUtils.substringAfter(route.getId(),":");
            if(StringUtils.equals(id,routeDefinition.getId())){
                throw new RouteException(String.format("%s 路由已有定义",routeDefinition.getId()));
            }
        }
        String id = String.format("%:%",tenantId,routeDefinition.getId());
        routeDefinition.setId(id);
        routeList.add(routeDefinition);
        saveRouteDefinitions(routeList);
    }

    /**
     * 更新租户路由定义
     * @param tenantId
     * @param routeDefinition
     */
    public void updateTenantRouteDefinition(String tenantId, RouteDefinition routeDefinition){
        if(routeDefinition == null) throw new RouteException("routeDefinition 不能为空");
        String routeId = String.format("%:%",tenantId,routeDefinition.getId());
        Map<String,RouteDefinition> routeDefinitionMap = getNacosRouteDefinitionMap();
        routeDefinition.setId(routeId);
        routeDefinitionMap.put(routeId,routeDefinition);
        saveRouteDefinitions(routeDefinitionMap.values());
    }

    /**
     * 移除租户路由定义
     * @param tenantId
     * @param id
     */
    public void removeTenantRouteDefinition(String tenantId,String id){
        String routeId = String.format("%:%",tenantId,id);
        Map<String,RouteDefinition> routeDefinitionMap = getNacosRouteDefinitionMap();
        routeDefinitionMap.remove(routeId);
        saveRouteDefinitions(routeDefinitionMap.values());
    }
}
